﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Http;
using System.Net.Http.Handlers;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Yxinmt.Utility;

namespace Yxinmt.Windows.Meeting
{
	public class RequestService
	{
		static readonly string _AppId = "b58c579f-7288-3169-a259-5a5c03765629";
		static readonly string _AppToken = "4GHORJQRNRhL7I7XYZnctDfwoEGDAYUNyknzU4fBv7z49FgWHIB1yg==";

		public static string Token { get; set; }

		public static long? TimeDiff { get; set; }

		public static string ServeiceUrl { get; set; }

		public static Dictionary<string, object> GetRequest(string url, ProgressMessageHandler progressMessageHandler, Dictionary<string, string> headers, int timeout = 0, bool isLog = false, bool isThrowException = true)
		{
			Dictionary<string, string> dict = new Dictionary<string, string>();
			url = ServeiceUrl + "/api/" + url;
			var response = Request(url, null, progressMessageHandler, HttpMethod.Get, headers, timeout);
			dict["Url"] = url;
			if (headers != null) dict["Headers"] = Utility.Common.ToJson(headers);
			dict["ResponseContent"] = response;
			return getResponse(isLog, dict, response, isThrowException);
		}

		static Dictionary<string, object> getResponse(bool isLog, Dictionary<string, string> dict, string response, bool isThrowException = true)
		{
			var res = Utility.Common.JsonToDictionary(response);
			if (res.ContainsKey("Token")) Token = res.GetStringValue("Token");

			var msg = res.GetStringValue("Message");
			if (res.ContainsKey("Ack"))
			{
				msg = res.GetValue<Dictionary<string, object>>("Ack").GetStringValue("Message");
			}
			if (!string.IsNullOrEmpty(msg) && isThrowException)
			{
				LoggerProxy.WriteLog("Message", "RequestService", "Request", dict);
				throw new Exception(msg);
			}
			else if (isLog) LoggerProxy.Info("RequestService", "Request", dict);
			return res;
		}

		static string Request(string url, HttpContent content, ProgressMessageHandler progressMessageHandler, HttpMethod method, Dictionary<string, string> headers, int timeout = 0)
		{
			url = Utility.Common.AddUrlRandom(url);

			HttpClient client = progressMessageHandler == null ? new HttpClient() : new HttpClient(progressMessageHandler);
			if (timeout > 0) client.Timeout = new TimeSpan(0, timeout, 0);

			HttpRequestMessage request = new HttpRequestMessage(method, url);

			if (content != null) request.Content = content;
			if (headers != null) foreach (var h in headers) request.Headers.Add(h.Key, h.Value);

			Task<HttpResponseMessage> response = client.SendAsync(request);

			return response.Result.Content.ReadAsStringAsync().Result;
		}

		static string UploadRequest(string url, HttpContent content, ProgressMessageHandler progressMessageHandler, HttpMethod method, Dictionary<string, string> headers, CancellationToken cancellationToken = default)
		{
			url = Utility.Common.AddUrlRandom(url);

			HttpClient client = progressMessageHandler == null ? new HttpClient() : new HttpClient(progressMessageHandler);
			client.Timeout = new TimeSpan(0, 20, 0);

			HttpRequestMessage request = new HttpRequestMessage(method, url);

			if (content != null) request.Content = content;
			if (headers != null) foreach (var h in headers) request.Headers.Add(h.Key, h.Value);

			Task<HttpResponseMessage> response = client.SendAsync(request, HttpCompletionOption.ResponseContentRead, cancellationToken);

			return response.Result.Content.ReadAsStringAsync().Result;
		}

		public static Dictionary<string, object> PostRequest(string url, ProgressMessageHandler progressMessageHandler, Dictionary<string, string> headers, object data, bool isLog, bool isThrowException)
		{
			return RequestByMethod(HttpMethod.Post, url, progressMessageHandler, headers, data, isLog, isThrowException);
		}

		public static Dictionary<string, object> PutRequest(string url, ProgressMessageHandler progressMessageHandler, Dictionary<string, string> headers, object data, bool isLog, bool isThrowException)
		{
			return RequestByMethod(HttpMethod.Put, url, progressMessageHandler, headers, data, isLog, isThrowException);
		}

		public static Dictionary<string, object> RequestByMethod(HttpMethod method, string url, ProgressMessageHandler progressMessageHandler, Dictionary<string, string> headers, object data, bool isLog, bool isThrowException)
		{
			Dictionary<string, string> dict = new Dictionary<string, string>();
			url = ServeiceUrl + "/api/" + url;
			string requestContent = Utility.Common.ToJson(data);
			var response = Request(url, new StringContent(requestContent, Encoding.UTF8, "application/json"), progressMessageHandler, method, headers);
			dict["Url"] = url;
			if (headers != null) dict["Headers"] = Utility.Common.ToJson(headers);
			dict["RequestContent"] = requestContent;
			dict["ResponseContent"] = response;
			return getResponse(isLog, dict, response, isThrowException);
		}

		static Dictionary<string, string> getHeaders(bool isAppToken = false)
		{
			Dictionary<string, string> headers = new Dictionary<string, string>();
			var token = isAppToken || string.IsNullOrEmpty(Token) ? _AppToken : Token;
			headers.Add("Token", token);
			headers.Add("Access-Token", getAccessToken(token));
			return headers;
		}

		static string getAccessToken(string token)
		{
			long time = Utility.Common.GetDateTotalMilliseconds(DateTime.Now);
			if (TimeDiff != null) time += TimeDiff.Value;
			var md5str = Utility.Common.ComputeStringMd5(_AppId + time + token);
			return Utility.Common.ToBase64String(_AppId + '@' + time + "@" + md5str);
		}

		public static long GetCurrentTime(string serviceUrl)
		{
			try
			{
				if (!serviceUrl.ToLower().StartsWith("http://")) serviceUrl = "http://" + serviceUrl;
				ServeiceUrl = serviceUrl.TrimEnd('/');
				string url = "Default/GetCurrentTime";
				var res = GetRequest(url, null, new Dictionary<string, string>(), 1000);
				long time = res.GetValue<long>("Time");
				TimeDiff = time - Utility.Common.GetDateTotalMilliseconds(DateTime.Now);
				return time;
			}
			catch
			{
				ServeiceUrl = string.Empty;
				return 0;
			}
		}

		public static Entity.UserInfo GetUserInfo(Entity.UserInfo userInfo)
		{
			string url = "VisitUser2/GetUserInfo";
			Dictionary<string, object> dict = new Dictionary<string, object>();
			dict["VisitUser2"] = userInfo;
			var res = PostRequest(url, null, getHeaders(true), dict, false, true);
			if (res.ContainsKey("Data")) return Utility.Common.DictionaryTo<Entity.UserInfo>(res.GetValue<Dictionary<string, object>>("Data"));
			else return null;
		}

		public static Entity.Response SaveRecord(Dictionary<string, object> record, bool isUpate)
		{
			string entityName = "MeetingRecord";
			string url = entityName;
			if (isUpate) url += $"({record.GetStringValue("RecordId")})";
			Dictionary<string, object> dict = new Dictionary<string, object>();
			dict[entityName] = record;
			Dictionary<string, object> res = null;
			if (isUpate) res = PutRequest(url, null, getHeaders(), dict, false, false);
			else res = PostRequest(url, null, getHeaders(), dict, false, false);
			return Utility.Common.DictionaryTo<Entity.Response>(res);
		}

		public static Entity.UpdateResponse UpdateFileName(Guid fileId, string fileName)
		{
			Dictionary<string, object> fileInfo = new Dictionary<string, object>();
			fileInfo["FileName"] = fileName;
			string url = $"MeetingSubjectFile({fileId})";
			Dictionary<string, object> dict = new Dictionary<string, object>();
			dict["MeetingSubjectFile"] = fileInfo;
			var res = PutRequest(url, null, getHeaders(), dict, false, false);
			return Utility.Common.DictionaryTo<Entity.UpdateResponse>(res);
		}

		public static List<Entity.MeetingRoom> GetMeetingRoomList()
		{
			string url = "VisitUser2/GetMeetingRooms";

			var res = GetRequest(url, null, getHeaders(true), 0, false);
			if (res.ContainsKey("Data")) return Utility.Common.DictionaryListToList<Entity.MeetingRoom>(res.GetValue<List<Dictionary<string, object>>>("Data"));
			else return null;
		}

		public static Entity.Meeting GetMeetingSubjects(Guid meetingId)
		{
			string url = $"MeetingSubject/GetMeetingSubjects({meetingId})?IsList=true";

			var res = GetRequest(url, null, getHeaders(), 0, false);
			if (res.ContainsKey("Data")) return Utility.Common.DictionaryTo<Entity.Meeting>(res.GetValue<Dictionary<string, object>>("Data"));
			else return null;
		}

		public static List<Entity.MeetingRecord> GetMeetingRecords(Guid meetingId)
		{
			string url = $"ViewMeetingRecord?$select=RecordId,RecordType,FileName,FileName2,FileType2,FileUrl,ImageUrl,ContentUrl,FileType,CreateDate,DisplayIndex&$filter=MeetingId eq @MeetingId&@MeetingId={meetingId}&$orderby=CreateDate desc";

			var res = GetRequest(url, null, getHeaders(), 0, false);
			if (res.ContainsKey("Data")) return Utility.Common.DictionaryListToList<Entity.MeetingRecord>(res.GetValue<List<Dictionary<string, object>>>("Data"));
			else return null;
		}

		public static Entity.MeetingRecord GetMeetingRecord(Guid recordId)
		{
			string url = $"MeetingRecord({recordId})";

			var res = GetRequest(url, null, getHeaders(), 0, false);
			if (res.ContainsKey("Data")) return Utility.Common.DictionaryTo<Entity.MeetingRecord>(res.GetValue<Dictionary<string, object>>("Data"));
			else return null;
		}

		static ProgressMessageHandler getProgressMessageHandler(Action<long, long, int> progressCallback)
		{
			if (progressCallback == null) return null;

			var hander = new HttpClientHandler();
			var progressHandler = new ProgressMessageHandler(hander);
			progressHandler.HttpSendProgress += (sender, e) =>
			{
				if (progressCallback != null && e.TotalBytes != null)
				{
					progressCallback(e.BytesTransferred, e.TotalBytes.Value, e.ProgressPercentage);
				}
			};
			return progressHandler;
		}

		public static Entity.UploadResponse UploadFile(string fileName, string dirName, string urlName, CancellationToken cancellationToken, System.IO.Stream stream, Action<long, long, int> progressCallback = null)
		{
			string url = $"uploadFile2?dirName={System.Web.HttpUtility.UrlEncode(dirName)}&urlName={System.Web.HttpUtility.UrlEncode(urlName)}";
			return UploadFile(url, getProgressMessageHandler(progressCallback), getHeaders(), fileName, stream, true, cancellationToken);
		}

		public static Entity.UploadResponse UploadFile(string fileName, string dirName, string urlName, byte[] bs)
		{
			string url = $"uploadFile2?dirName={System.Web.HttpUtility.UrlEncode(dirName)}&urlName={System.Web.HttpUtility.UrlEncode(urlName)}";
			return UploadFile(url, getHeaders(), fileName, bs, true, CancellationToken.None, null);
		}

		public static Entity.UploadResponse UploadFile(string url, Dictionary<string, string> headers, string fileName, byte[] bs, bool isLog, CancellationToken cancellationToken, Action<long, long, int> progressCallback = null)
		{
			return UploadFile(url, getProgressMessageHandler(progressCallback), headers, fileName, new System.IO.MemoryStream(bs), isLog, cancellationToken);
		}

		public static Entity.UploadResponse UploadFile(string url, ProgressMessageHandler progressMessageHandler, Dictionary<string, string> headers, string fileName, System.IO.Stream stream, bool isLog, CancellationToken cancellationToken)
		{
			Dictionary<string, string> dict = new Dictionary<string, string>();
			url = ServeiceUrl + "/api/" + url;
			using (var form = new MultipartFormDataContent())
			{
				using (var streamContent = new StreamContent(stream))
				{
					form.Add(streamContent, "file", fileName);
					var response = UploadRequest(url, form, progressMessageHandler, HttpMethod.Post, headers, cancellationToken);
					dict["Url"] = url;
					dict["Headers"] = Utility.Common.ToJson(headers);
					dict["ResponseContent"] = response;
					var res = getResponse(isLog, dict, response);
					return Utility.Common.DictionaryTo<Entity.UploadResponse>(res);
				}
			}
		}

		public static Entity.Meeting GetMeetingDeviceUser(Guid meetingId)
		{
			string url = $"Meeting/GetMeetingDeviceUser({meetingId})";

			var res = GetRequest(url, null, getHeaders(), 0, false);
			if (res.ContainsKey("Data")) return Utility.Common.DictionaryTo<Entity.Meeting>(res.GetValue<Dictionary<string, object>>("Data"));
			else return null;
		}

		public static List<Entity.FileInfo> GetMeetingVideos(Guid meetingId)
		{
			string url = $"ViewMeetingVideo?$select=VideoType,FileId,FileName,FileUrl,FileMd5,FileSize,FileType&$filter=MeetingId eq @MeetingId&@MeetingId={meetingId}&$orderby=VideoType,DisplayIndex";

			var res = GetRequest(url, null, getHeaders(), 0, false);
			if (res.ContainsKey("Data")) return Utility.Common.DictionaryListToList<Entity.FileInfo>(res.GetValue<List<Dictionary<string, object>>>("Data"));
			else return null;
		}

		public static Entity.Response InsertMeetingSubjectFileSlice(Entity.MeetingSubjectFileSlice slice)
		{
			string entityName = "MeetingSubjectFileSlice";
			string url = entityName;

			Dictionary<string, object> dict = new Dictionary<string, object>();
			dict[entityName] = slice;
			Dictionary<string, object> res = null;
			res = PostRequest(url, null, getHeaders(), dict, true, false);
			return Utility.Common.DictionaryTo<Entity.Response>(res);
		}

		public static Entity.Response SliceToFile(Entity.UploadFile file)
		{
			string url = "MeetingSubjectFile/SliceToFile";

			Dictionary<string, object> dict = new Dictionary<string, object>();
			dict["MeetingSubjectFile"] = file;
			Dictionary<string, object> res = null;
			res = PostRequest(url, null, getHeaders(), dict, true, false);
			return Utility.Common.DictionaryTo<Entity.Response>(res);
		}

		public static List<Entity.MeetingSubjectFileSlice> GetMeetingSubjectFileSlices(Guid fileId)
		{
			string url = $"MeetingSubjectFileSlice?$select=SliceId,FileId,DisplayIndex,FileUrl,FileSize&$filter=FileId eq @FileId&@FileId={fileId}&$orderby=DisplayIndex";

			var res = GetRequest(url, null, getHeaders(), 0, false);
			if (res.ContainsKey("Data")) return Utility.Common.DictionaryListToList<Entity.MeetingSubjectFileSlice>(res.GetValue<List<Dictionary<string, object>>>("Data"));
			else return null;
		}

		public static bool JudgeExists(string dirName, string urlName)
		{
			string url = $"judgeExists?dirName={System.Web.HttpUtility.UrlEncode(dirName)}&urlName={System.Web.HttpUtility.UrlEncode(urlName)}";
			var res = GetRequest(url, null, getHeaders(), 0, false);
			return res.GetValue<bool>("IsExists");
		}
	}
}

